﻿using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml;

namespace BuddHa.Wechat.Mini.Protocols
{
    public class GatewayData
    {
        class GData : DynamicObject
        {
            public GData(SortedDictionary<string, object> keyValuePairs)
            {
                Values = keyValuePairs;
            }

            public SortedDictionary<string, object> Values { get; }

            public override bool TryGetMember(GetMemberBinder binder, out object result)
            {
                return Values.TryGetValue(binder.Name, out result);
            }

            public override bool TrySetMember(SetMemberBinder binder, object value)
            {
                Values[binder.Name] = value;
                return true;
            }
        }

        private GData _data;
        public dynamic Data
        {
            get
            {
                return _data;
            }
        }

        public object this[string key]
        {
            get => _data.Values[key];
            set => _data.Values[key] = value;
        }

        public KeyValuePair<string, object> this[int index]
        {
            get => _data.Values.ElementAt(index);
        }

        private string _orginData;
        public string OriginData
        {
            get
            {
                return _orginData;
            }
        }


        public GatewayData() : this(StringComparer.Ordinal)
        {

        }

        public GatewayData(IComparer<string> comparer)
        {
            _data = new GData(new SortedDictionary<string, object>(comparer));
        }

        /// <summary>
        /// 判断数据是否存在
        /// </summary>
        /// <param name="key">键</param>
        /// <returns></returns>
        public bool Exists(string key) => _data.Values.ContainsKey(key);

        /// <summary>
        /// 添加网关参数
        /// </summary>
        /// <param name="key">键</param>
        /// <param name="value">值</param>
        /// <returns></returns>
        public bool Add(string key, object value)
        {
            _orginData = null;
            if (string.IsNullOrEmpty(key))
            {
                throw new ArgumentNullException("key", "参数名不能为空");
            }

            if (value is null || string.IsNullOrEmpty(value.ToString()))
            {
                return false;
            }

            if (Exists(key))
            {
                _data.Values[key] = value;
            }
            else
            {
                _data.Values.Add(key, value);
            }
            return true;
        }

        /// <summary>
        /// 添加网关
        /// </summary>
        /// <param name="gatewayData"></param>
        public void AddRange(GatewayData gatewayData)
        {
            foreach (var item in gatewayData._data.Values)
            {
                Add(item.Key, item.Value);
            }
        }

        /// <summary>
        /// 根据参数名获取参数值
        /// </summary>
        /// <param name="key">参数名</param>
        /// <returns>参数值</returns>
        public object GetValue(string key)
        {
            _data.Values.TryGetValue(key, out object value);
            return value;
        }

        /// <summary>
        /// 根据参数名获取参数值
        /// </summary>
        /// <param name="key">参数名</param>
        /// <returns>参数值</returns>
        public string GetStringValue(string key)
        {
            return GetValue(key)?.ToString();
        }

        /// <summary>
        /// 清空网关数据
        /// </summary>
        public void Clear()
        {
            _data.Values.Clear();
        }

        /// <summary>
        /// 移除指定参数
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool Remove(string key)
        {
            return _data.Values.Remove(key);
        }

        /// <summary>
        /// 将网关数据转成Xml格式数据
        /// </summary>
        /// <returns></returns>
        public string ToXml()
        {
            if (_data.Values.Count == 0)
            {
                return string.Empty;
            }
            var sb = new StringBuilder();
            sb.Append("<xml>");
            foreach (var item in _data.Values)
            {
                if (item.Value is string)
                {
                    sb.AppendFormat("<{0}><![CDATA[{1}]]></{0}>", item.Key, item.Value);
                }
                else
                {
                    sb.AppendFormat("<{0}>{1}</{0}>", item.Key, item.Value);
                }
            }
            sb.Append("</xml>");

            return sb.ToString();
        }

        /// <summary>  
        /// </summary>
        /// <param name="xml">Xml数据</param>
        /// <returns></returns>
        public void FromXml(string xml)
        {
            _data.Values.Clear();
            try
            {
                var xmlDoc = new XmlDocument()
                {
                    XmlResolver = null
                };
                xmlDoc.LoadXml(xml);
                var xmlElement = xmlDoc.DocumentElement;
                var nodes = xmlElement.ChildNodes;
                foreach (var item in nodes)
                {
                    var xe = (XmlElement)item;
                    if (xe.ChildNodes.Count == 1)
                    {
                        if (xe.ChildNodes[0].NodeType.Equals(XmlNodeType.CDATA))
                        {
                            Add(xe.Name, xe.InnerText);
                        }
                        else
                        {
                            if (isNumber(xe.InnerText))
                            {
                                Add(xe.Name, Convert.ToInt32(xe.InnerText));
                            }
                            else
                            {
                                Add(xe.Name, xe.InnerText);
                            }
                        }
                    }
                }
            }
            finally
            {
                _orginData = xml;
            }
        }

        /// <summary>
        /// 字符串是否为数字字符串
        /// </summary>
        public bool isNumber(string message)
        {
            try
            {
                var result = Convert.ToInt32(message);
                return true;
            }
            catch
            {
                return false;
            }

        }

        /// <summary>
        /// 将网关数据转换为Url格式数据
        /// </summary>
        /// <param name="isUrlEncode">是否需要url编码</param>
        /// <returns></returns>
        public string ToUrl(bool isUrlEncode = true)
        {
            return string.Join("&",
                _data.Values
                .Select(selector => $"{selector.Key}={(isUrlEncode ? WebUtility.UrlEncode(selector.Value.ToString()) : selector.Value.ToString())}"));
        }

        /// <summary>
        /// 将Url格式数据转换为网关数据
        /// </summary>
        /// <param name="url">url数据</param>
        /// <param name="isUrlDecode">是否需要url解码</param>
        /// <returns></returns>
        public void FromUrl(string url, bool isUrlDecode = true)
        {
            try
            {
                Clear();
                if (!string.IsNullOrEmpty(url))
                {
                    int index = url.IndexOf('?');

                    if (index == 0)
                    {
                        url = url.Substring(index + 1);
                    }
                    var regex = new Regex(@"(^|&)?(\w+)=([^&]+)(&|$)?", RegexOptions.Compiled);
                    var mc = regex.Matches(url);

                    foreach (Match item in mc)
                    {
                        string value = item.Result("$3");
                        Add(item.Result("$2"), isUrlDecode ? WebUtility.UrlDecode(value) : value);
                    }
                }
            }
            finally
            {
                _orginData = url;
            }
        }

        /// <summary>
        /// 将表单数据转换为网关数据
        /// </summary>
        /// <param name="form">表单</param>
        /// <returns></returns>
        public void FromForm(string form, bool isUrlDecode = true)
        {
            try
            {
                Clear();

                foreach (var item in form.Split('&'))
                {
                    var keyAndValue = item.Split('=');
                    if (keyAndValue.Length == 2)
                    {
                        var value = keyAndValue[1];
                        Add(keyAndValue[0], isUrlDecode ? WebUtility.UrlDecode(value) : value);
                    }
                }
            }
            finally
            {
                _orginData = form.ToString();
            }
        }

        /// <summary>
        /// 将网关数据转换为表单数据
        /// </summary>
        /// <param name="url">请求地址</param>
        /// <returns></returns>
        public string ToForm(string url)
        {
            var html = new StringBuilder();
            html.Append($"<form name='gateway' method='post' action ='{url}'>");
            foreach (var item in _data.Values)
            {
                html.Append($"<input type='hidden' name='{item.Key}' value='{item.Value}'/>");
            }
            html.Append("</form>");
            html.Append("<script language='javascript' type='text/javascript'>");
            html.Append("document.forms['gateway'].submit();");
            html.Append("</script>");

            return html.ToString();
        }
    }
}
